package cn.bitkit.mybatis.base;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import cn.bitkit.base.container.PageRequest;
import cn.bitkit.base.container.PageResponse;
import cn.bitkit.mybatis.query.QueryCriteria;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;

import cn.bitkit.mybatis.SqlProvider;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.session.ResultHandler;

/**
 * @author changbo
 */
public interface BaseMapper<E> {
	/**
	 * 查询
	 * @param id 主键
	 * @return 实体类
	 */
	@SelectProvider(type = SqlProvider.class, method = "dynamicSql")
	E selectById(@Param("id") Serializable id);

	/**
	 * 查询
	 * @param criteria 查询条件
	 * @return 实体类
	 */
	@SelectProvider(type = SqlProvider.class, method = "dynamicSql")
	E select(@Param("criteria") QueryCriteria criteria);

	/**
	 * 查询
	 * @param criteria 查询条件
	 * @return 实体类
	 */
	@SelectProvider(type = SqlProvider.class, method = "dynamicSql")
	List<E> selectList(@Param("criteria") QueryCriteria criteria);

	/**
	 * 查询返回游标
	 * @param criteria 查询条件
	 * @return 游标
	 */
	@SelectProvider(type = SqlProvider.class, method = "dynamicSql")
	Cursor<E> selectCursor(@Param("criteria") QueryCriteria criteria);

	/**
	 * 查询列表并逐条处理
	 * @param criteria 查询条件
	 * @param handler 处理器
	 */
	@SelectProvider(type = SqlProvider.class, method = "dynamicSql")
	void selectHandler(@Param("criteria") QueryCriteria criteria, ResultHandler<E> handler);


	/**
	 * 查询
	 * @param list id列表
	 * @return 实体类
	 */
	@SelectProvider(type = SqlProvider.class, method = "dynamicSql")
	List<E> selectListByIdList(@Param("idList") List<? extends Serializable> list);

	/**
	 * 查询
	 * @param criteria 查询条件
	 * @return 数量
	 */
	@SelectProvider(type = SqlProvider.class, method = "dynamicSql")
	long selectCount(@Param("criteria") QueryCriteria criteria);

	/**
	 * 仅查询分页数据（不包括总条数，需要查询总条数 @link selectPage(PageRequest<?> pageRequest)）
	 * @param criteria 查询条件
	 * @return 分页实体类
	 */
	@SelectProvider(type = SqlProvider.class, method = "dynamicSql")
	List<E> selectPage(@Param("criteria") QueryCriteria criteria);

	/**
	 * 插入
	 * @param param 实体类
	 * @return 插入条数
	 */
	@InsertProvider(type = SqlProvider.class, method = "dynamicSql")
	int insert(@Param("entity") E param);

	/**
	 * 批量插入
	 * @param list 实体类
	 * @return 插入条数
	 */
	@InsertProvider(type = SqlProvider.class, method = "dynamicSql")
	int insertList(@Param("list") List<E> list);

	/**
	 * 删除
	 * @param id 主键
	 * @return 删除条数
	 */
	@DeleteProvider(type = SqlProvider.class, method = "dynamicSql")
	int deleteById(@Param("id") Serializable id);

	/**
	 * 删除
	 * @param list 主键
	 * @return 删除条数
	 */
	@DeleteProvider(type = SqlProvider.class, method = "dynamicSql")
	int deleteByIdList(@Param("list") List<? extends Serializable> list);

	/**
	 * 删除
	 * @param criteria 查询条件
	 * @return 删除条数
	 */
	@DeleteProvider(type = SqlProvider.class, method = "dynamicSql")
	int delete(@Param("criteria") QueryCriteria criteria);

	/**
	 * 修改所有字段
	 * @param entity 实体（必须带id）
	 * @return 修改条数
	 */
	@UpdateProvider(type = SqlProvider.class, method = "dynamicSql")
	int updateById(@Param("entity") E entity);

	/**
	 * 修改分组字段
	 * @param entity 实体
	 * @param criteria 查询条件
	 * @return 修改条数
	 */
	@UpdateProvider(type = SqlProvider.class, method = "dynamicSql")
	int updateNonNull(@Param("entity") E entity, @Param("criteria") QueryCriteria criteria);

	/**
	 * 修改非空字段
	 * @param entity 实体（必须带id）
	 * @return 修改条数
	 */
	@UpdateProvider(type = SqlProvider.class, method = "dynamicSql")
	int updateNonNullById(@Param("entity") E entity);

	/**
	 * 修改非空字段
	 * @param entity 实体（必须带id）
	 * @param list 主键
	 * @return 修改条数
	 */
	@UpdateProvider(type = SqlProvider.class, method = "dynamicSql")
	int updateNonNullByIdList(@Param("entity") E entity, @Param("list") List<? extends Serializable> list);

	/**
	 * 修改分组字段
	 * @param entity 实体
	 * @param criteria 查询条件
	 * @param group 字段分组
	 * @return 修改条数
	 */
	@UpdateProvider(type = SqlProvider.class, method = "dynamicSql")
	int updateGroup(@Param("entity") E entity, @Param("criteria") QueryCriteria criteria, @Param("group") String group);

	/**
	 * 修改分组字段
	 * @param entity 实体（必须带id）
	 * @param group 字段分组
	 * @return 修改条数
	 */
	@UpdateProvider(type = SqlProvider.class, method = "dynamicSql")
	int updateGroupById(@Param("entity") E entity, @Param("group") String group);

	/**
	 * 修改分组字段
	 * @param entity 实体（必须带id）
	 * @param list 主键
	 * @param group 字段分组
	 * @return 修改条数
	 */
	@UpdateProvider(type = SqlProvider.class, method = "dynamicSql")
	int updateGroupByIdList(@Param("entity") E entity, @Param("list") List<? extends Serializable> list, @Param("group") String group);

	/**
	 * 分页查询
	 * @param pageRequest 查询条件
	 * @return 分页数据
	 */
	default PageResponse<E> selectPageResponse(PageRequest<?> pageRequest){
		QueryCriteria criteria = QueryCriteria.createPage(pageRequest);
		long count = this.selectCount(criteria);
		List<E> list = new ArrayList<>();
		if(count > criteria.getOffset()){
			list = this.selectPage(criteria);
		}
		return PageResponse.create(count, list);
	}


	/**
	 * 批量分批次插入(默认每次插入1000条)
	 * @param list 实体类
	 * @return 插入的数据
	 */
	default int insertListByBatch(List<E> list){
		return insertListByBatch(list, 1000);
	}

	/**
	 * 批量分批次插入
	 * @param list 实体类
	 * @param size 每个批次插入的数据
	 * @return 插入的数据
	 */
	default int insertListByBatch(List<E> list, int size){
		int max = list.size() / size;
		max = list.size() % size == 0 ? max : max + 1;
		for(int i = 0; i < max; i++){
			int end = Math.min((i + 1) * size, list.size());
			List<E> subList = list.subList(i * size, end);
			this.insertList(subList);
		}
		return list.size();
	}

}
